import BN from "../bn.js";
import elliptic from "../elliptic";
import parseKeys from "../parse-asn1";
import * as curves from "./curves.json";
var EC = elliptic.ec;
function verify(sig, hash, key, signType, tag) {
    var pub = parseKeys(key);
    if (pub.type === 'ec') {
        // rsa keys can be interpreted as ecdsa ones in openssl
        if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa')
            throw new Error('wrong public key type');
        return ecVerify(sig, hash, pub);
    }
    else if (pub.type === 'dsa') {
        if (signType !== 'dsa')
            throw new Error('wrong public key type');
        return dsaVerify(sig, hash, pub);
    }
    else {
        if (signType !== 'rsa' && signType !== 'ecdsa/rsa')
            throw new Error('wrong public key type');
    }
    hash = Buffer.concat([tag, hash]);
    var len = pub.modulus.byteLength();
    var pad = [1];
    var padNum = 0;
    while (hash.length + pad.length + 2 < len) {
        pad.push(0xff);
        padNum++;
    }
    pad.push(0x00);
    var i = -1;
    while (++i < hash.length) {
        pad.push(hash[i]);
    }
    pad = Buffer.from(pad);
    var red = BN.mont(pub.modulus);
    sig = new BN(sig).toRed(red);
    sig = sig.redPow(new BN(pub.publicExponent));
    sig = Buffer.from(sig.fromRed().toArray());
    var out = padNum < 8 ? 1 : 0;
    len = Math.min(sig.length, pad.length);
    if (sig.length !== pad.length)
        out = 1;
    i = -1;
    while (++i < len)
        out |= sig[i] ^ pad[i];
    return out === 0;
}
function ecVerify(sig, hash, pub) {
    var curveId = curves[pub.data.algorithm.curve.join('.')];
    if (!curveId)
        throw new Error('unknown curve ' + pub.data.algorithm.curve.join('.'));
    var curve = new EC(curveId);
    var pubkey = pub.data.subjectPrivateKey.data;
    return curve.verify(hash, sig, pubkey);
}
function dsaVerify(sig, hash, pub) {
    var p = pub.data.p;
    var q = pub.data.q;
    var g = pub.data.g;
    var y = pub.data.pub_key;
    var unpacked = parseKeys.signature.decode(sig, 'der');
    var s = unpacked.s;
    var r = unpacked.r;
    checkValue(s, q);
    checkValue(r, q);
    var montp = BN.mont(p);
    var w = s.invm(q);
    var v = g.toRed(montp)
        .redPow(new BN(hash).mul(w).mod(q))
        .fromRed()
        .mul(y.toRed(montp).redPow(r.mul(w).mod(q)).fromRed())
        .mod(p)
        .mod(q);
    return v.cmp(r) === 0;
}
function checkValue(b, q) {
    if (b.cmpn(0) <= 0)
        throw new Error('invalid sig');
    if (b.cmp(q) >= q)
        throw new Error('invalid sig');
}
export default verify;
